package com.zyh.scs.common.core.util;

import cn.hutool.core.util.RandomUtil;
import com.zyh.scs.common.core.exception.OpenException;
import com.zyh.scs.common.core.exception.OpenResponseCode;
import io.minio.*;
import io.minio.errors.MinioException;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

@Component
@Slf4j
public class MinioUtil {

    @Value("${minio.url:http://10.253.14.239:9000}")
    private String minioUrl;

    @Value("${minio.requestUrl:https://dc.chengdurail.com:8443/account/admin/file}")
    private String requestUrl;

    @Value("${minio.intranetUrl:https://dc.chengdurail.com:8443/account/admin/file}")
    private String intranetUrl;

    @Value("${minio.accessKey:dcxt}")
    private String accessKey;

    @Value("${minio.secretKey:Dcxt_New!2024}")
    private String secretKey;

    @Value("${minio.bucketName:scs}")
    private String defaultBucketName;

    private MinioClient minioClient;

    public synchronized MinioClient getMinioClient() {
        if (minioClient != null) {
            return minioClient;
        }
        try {
            minioClient = MinioClient.builder().endpoint(minioUrl).credentials(accessKey, secretKey).build();
            return minioClient;
        } catch (Exception e) {
            log.error("minioClient init error: {}", e.getMessage());
        }
        return null;
    }

    /**
     * 上传文件到MinIO，生成随机文件名以避免重名  外网
     *
     * @param inputStream 输入流
     * @param bucketName  存储桶名称
     * @return path 文件访问路径
     */
    public String uploadFile(String bucketName, InputStream inputStream, String path, String fileName, boolean useOriginalFileName) {
        try {
            if (!useOriginalFileName) {
                fileName = generateRandomFileName(fileName);
            }
            String filePath = buildPath(path, fileName);
            bucketName = bucketName != null ? bucketName : this.defaultBucketName;
            getMinioClient().putObject(PutObjectArgs.builder().bucket(bucketName)
                    .object(filePath).stream(inputStream, inputStream.available(), -1).build());
            log.info("filePath-->" + filePath);
            log.info("bucketName-->" + bucketName);
            return requestUrl + "/" + bucketName + filePath;
        } catch (Exception e) {
            throw new OpenException(OpenResponseCode.FILE_UPLOAD, e.getMessage());
        }

    }


    /**
     * 上传文件到MinIO，生成随机文件名以避免重名 内网
     *
     * @param inputStream 输入流
     * @param bucketName  存储桶名称
     * @return path 文件访问路径
     */
    public String uploadFileIntranet(String bucketName, InputStream inputStream, String path, String fileName, boolean useOriginalFileName) {
        try {
            if (!useOriginalFileName) {
                fileName = generateRandomFileName(fileName);
            }
            String filePath = buildPath(path, fileName);
            bucketName = bucketName != null ? bucketName : this.defaultBucketName;
            getMinioClient().putObject(PutObjectArgs.builder().bucket(bucketName)
                    .object(filePath).stream(inputStream, inputStream.available(), -1).build());
            log.info("filePath Intranet-->" + filePath);
            log.info("bucketName Intranet-->" + bucketName);
            return intranetUrl + "/" + bucketName + filePath;
        } catch (Exception e) {
            throw new OpenException(OpenResponseCode.FILE_UPLOAD, e.getMessage());
        }

    }

    public String uploadFile(String bucketName, byte[] bytes, String path, String originalFileName) {
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
            return uploadFile(bucketName, inputStream, path, originalFileName, false);
        } catch (Exception e) {
            throw new OpenException(OpenResponseCode.FILE_UPLOAD, e.getMessage());
        }
    }

    public String uploadFile(InputStream inputStream, String path, String originalFileName) {
        try {
            return uploadFile(null, inputStream, path, originalFileName, false);
        } catch (Exception e) {
            throw new OpenException(OpenResponseCode.FILE_UPLOAD, e.getMessage());
        }
    }

    public String uploadFile(InputStream inputStream, String path, String originalFileName, boolean useOriginalFileName) {
        try {
            return uploadFile(null, inputStream, path, originalFileName, useOriginalFileName);
        } catch (Exception e) {
            throw new OpenException(OpenResponseCode.FILE_UPLOAD, e.getMessage());
        }
    }

    /**
     * 获取文件baseurl
     */
    public String getBaseUrl() {
        return this.minioUrl;
    }

    /**
     * 生成随机文件名
     *
     * @param originalName 原始文件名
     * @return 新的随机文件名
     */
    private static String generateRandomFileName(String originalName) {
//        String fileNameWithoutExtension = originalName.substring(0, originalName.lastIndexOf('.'));
        String extension = originalName.substring(originalName.lastIndexOf('.'));
        return RandomUtil.randomString(20) + extension;
    }

    /**
     * 设置存储桶为公开读取
     *
     * @param bucketName 存储桶名称
     */
    public void setBucketPublicRead(String bucketName) {
        try {
            this.getMinioClient().setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config("{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}").build());
        } catch (Exception e) {
            throw new RuntimeException("Failed to set bucket policy to public read", e);
        }
    }

    // 以下是MinioClient的代理方法，用于内部使用
    public void putObject(PutObjectArgs args) throws MinioException, IOException, NoSuchAlgorithmException, InvalidKeyException {
        minioClient.putObject(args);
    }

    public void setBucketPolicy(SetBucketPolicyArgs args) throws MinioException, IOException, NoSuchAlgorithmException, InvalidKeyException {
        minioClient.setBucketPolicy(args);
    }

    public String getBucketName() {
        return this.defaultBucketName;
    }

    public GetObjectResponse getObject(String bucketName, String fileUrl) {
        try {
            GetObjectResponse object = this.getMinioClient().getObject(GetObjectArgs.builder().bucket(bucketName).object(fileUrl).build());
            return object;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void removeObject(String bucketName, String path, String fileName) {
        String filePath = buildPath(path, fileName);
        try {
            this.getMinioClient().removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(filePath).build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static @NotNull String buildPath(String path, String fileName) {
        if (path == null) {
            path = "default";
        }
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        if (path.endsWith("/")) {
            path = path.substring(0, path.length() - 1);
        }
        String filePath = "/" + path + "/" + fileName;
        return filePath;
    }
}